from main.dataStructures import ProcessesQueue
from main.utils import detectIOResource
from main.processHandling import updatePCB
from threading import Thread
from time import sleep



class IO(Thread):
    def __init__(self):
        Thread.__init__(self)
        self.processesWaitingForIO = ProcessesQueue()
        self.dispatchingPolicy = IODispatcherPolicy_FCFS() #PARA QUE EN UN FUTURO PUEDAN SER INTERCAMBIABLES
        self.devices = [] # MUST BE A Collection
        self.processesUsingADevice = ProcessesQueue()
        
    def dispatch(self):
        #Toma una instruccion de la ioQueue y le busca el device que necesita, y se lo asigna
        current = self.dispatchingPolicy.getAProcess(self.processesWaitingForIO)
        device = detectIOResource(current.pcb.currentInstruction,self.devices)
        device.currentProcess = current
        self.processesUsingADevice.append(current) 
        device.available = False
  
        
    def addIOProcessToQueue(self,aProcess):
        self.processesWaitingForIO.append(aProcess)
    
    def startDevices(self):
        for dev in self.devices:
            dev.start()
        
    def run(self):
        self.startDevices()
        while(True):
            print('IO NOT DISTPATCHING')
            if(self.processesWaitingForIO.length()>0):
                self.dispatch()
                print('IO DISPATCHED')
            sleep(1)
            
        

#----------------------------------------------------------------------------------------#
#----------------------SCHEDULLING POLICIES----------------------------------------------#
#----------------------------------------------------------------------------------------#

class IODispatcherPolicy():
    
    def getAProcess(self,aProcessesQueue):
        raise NotImplementedError


class IODispatcherPolicy_FCFS(IODispatcherPolicy):
    
    def __init__(self):
        IODispatcherPolicy.__init__(self)
        
    
    def getAProcess(self, aProcessesQueue):
        return aProcessesQueue.pop(0)# es fifo toma el primero


#----------------------------------------------------------------------------------------#
#----------------------IO DEVICES--------------------------------------------------------#
#----------------------------------------------------------------------------------------#


#-----------ABSTRACT-------------#

class IODevice(Thread):
    def __init__(self,anID, aKernel,aName,aQueueProcesess):
        Thread.__init__(self)
        self.name = aName
        self.id = anID
        self.available = True
        self.currentProcess = None
        self.kernel = aKernel
        self.processesUsingADevice = aQueueProcesess
    
    def setAsUnavailable(self):
        self.available = False
    
    def setAsAvailable(self):
        self.available = True
    
    def executeInstruction(self,aProcess):
        pass
    
    def execute(self,aProcess):
        self.executeInstruction(aProcess)
        #ACTUALIZO EL PCB
        updatePCB(aProcess)
        # ACA VER SI LA SIGUIENTE ES IO DIRECTO QUEDARSE (DISCUTIR EN CLASE)
        outgoingProcess = self.currentProcess
        self.processesUsingADevice.cut( outgoingProcess)
        self.currentProcess = None
        self.setAsAvailable()#termino de ejecutar esta libre de nuevo
        self.kernel.interrupt_processFinishedIOInstruction(outgoingProcess)
    
    def run(self):
        while(True):
            if(not self.available):
                self.execute(self.currentProcess)                
            
        

#--------CONCRETES-------------#

class Printer(IODevice):
    
    def __init__(self,anID,aKernel,aQueueProcesess):
        IODevice.__init__(self, anID, aKernel,'Printer',aQueueProcesess)
        
    def printSomething(self, aSomething):
        print(self.name+' id:'+str(self.id)+' PRINTING: '+str(aSomething.pcb.processID))
        #DEVICE HACER INTERRUPCION AL KERNEL
    
    def executeInstruction(self,aProcess):
        self.printSomething(aProcess)
        sleep(aProcess.pcb.currentInstruction.timeRemaining)
        
        